#ifndef _PCIe_ENDPOINT_H
#define _PCIe_ENDPOINT_H

#include "PCIe_types.h"

class PCIe_Endpoint
{
protected:
	PCIe_TLP
	construct_TLP_Request_Memory_64(bool with_data_,
									PCIe_TLP_type_t type_,
									byte traffic_class_,
									bool TLP_digest_,
									bool error_poisoned_,
									bool relexed_ordering_,
									bool no_snoop_,
									unsigned short length_,
									byte bus_number_,
									byte device_number_,
									byte function_number_,
									byte tag_,
									byte BE_,
									unsigned high_address_,
									unsigned low_address_,
									unsigned* data_,
									unsigned ECRC_)
	{
		PCIe_TLP return_TLP;

		return_TLP.with_data = with_data_;
		return_TLP.DW4_header = true;

		return_TLP.header = new byte[PCIe_DOUBLE_WORD*4];

		return_TLP.header[0] = 0x0;
		if (with_data_)
			return_TLP.header[0] = return_TLP.header[0] | 0x40;
		return_TLP.header[0] = return_TLP.header[0] | 0x20;
		return_TLP.header[0] = return_TLP.header[0] | (byte)(0x1F & type_);
		return_TLP.header[1] = (byte)(0x70 & (traffic_class_<<5));
		return_TLP.header[2] = 0x0;
		if (TLP_digest_)
			return_TLP.header[2] = return_TLP.header[2] | 0x80;
		if (error_poisoned_)
			return_TLP.header[2] = return_TLP.header[2] | 0x40;
		if (relexed_ordering_)
			return_TLP.header[2] = return_TLP.header[2] | 0x20;
		if (no_snoop_)
			return_TLP.header[2] = return_TLP.header[2] | 0x10;
		return_TLP.header[2] = return_TLP.header[2] | (byte)(length_>>8);
		return_TLP.header[3] = (byte)(length_);
		return_TLP.header[4] = bus_number_;
		return_TLP.header[5] = (device_number_<<3) | (0x7 & function_number_);
		return_TLP.header[6] = tag_;
		return_TLP.header[7] = BE_;
		return_TLP.header[8] = (byte)(high_address_>>24);
		return_TLP.header[9] = (byte)(high_address_>>16);
		return_TLP.header[10] = (byte)(high_address_>>8);
		return_TLP.header[11] = (byte)(high_address_);
		return_TLP.header[12] = (byte)(low_address_>>24);
		return_TLP.header[13] = (byte)(low_address_>>16);
		return_TLP.header[14] = (byte)(low_address_>>8);
		return_TLP.header[15] = (byte)(low_address_);
	
		return_TLP.length = length_;
		if (with_data_)
		{
			if (length_ == 0)
			{
				return_TLP.data = new unsigned[1024];
				for (int i=0;i<1024;i++)
					return_TLP.data[i] = data_[i];
			}
			else
			{
				return_TLP.data = new unsigned[length_];
				for (int i=0;i<length_;i++)
					return_TLP.data[i] = data_[i];
			}
		}
		else
		{
			return_TLP.data = NULL;
		}

		return_TLP.TLP_digest = TLP_digest_;
		if (TLP_digest_)
			return_TLP.ECRC = ECRC_;

		return return_TLP;
	}

	PCIe_TLP
	construct_TLP_Request_Memory_32(bool with_data_,
									PCIe_TLP_type_t type_,
									byte traffic_class_,
									bool TLP_digest_,
									bool error_poisoned_,
									bool relexed_ordering_,
									bool no_snoop_,
									unsigned short length_,
									byte bus_number_,
									byte device_number_,
									byte function_number_,
									byte tag_,
									byte BE_,
									unsigned address_,
									unsigned* data_,
									unsigned ECRC_)
	{
		PCIe_TLP return_TLP;

		return_TLP.with_data = with_data_;
		return_TLP.DW4_header = false;

		return_TLP.header = new byte[PCIe_DOUBLE_WORD*3];

		return_TLP.header[0] = 0x0;
		if (with_data_)
			return_TLP.header[0] = return_TLP.header[0] | 0x40;
		return_TLP.header[0] = return_TLP.header[0] | (byte)(0x1F & type_);
		return_TLP.header[1] = (byte)(0x70 & (traffic_class_<<5));
		return_TLP.header[2] = 0x0;
		if (TLP_digest_)
			return_TLP.header[2] = return_TLP.header[2] | 0x80;
		if (error_poisoned_)
			return_TLP.header[2] = return_TLP.header[2] | 0x40;
		if (relexed_ordering_)
			return_TLP.header[2] = return_TLP.header[2] | 0x20;
		if (no_snoop_)
			return_TLP.header[2] = return_TLP.header[2] | 0x10;
		return_TLP.header[2] = return_TLP.header[2] | (byte)(length_>>8);
		return_TLP.header[3] = (byte)(length_);
		return_TLP.header[4] = bus_number_;
		return_TLP.header[5] = (device_number_<<3) | (0x7 & function_number_);
		return_TLP.header[6] = tag_;
		return_TLP.header[7] = BE_;
		return_TLP.header[8] = (byte)(address_>>24);
		return_TLP.header[9] = (byte)(address_>>16);
		return_TLP.header[10] = (byte)(address_>>8);
		return_TLP.header[11] = (byte)(address_);

		return_TLP.length = length_;
		if (with_data_)
		{
			if (length_ == 0)
			{
				return_TLP.data = new unsigned[1024];
				for (int i=0;i<1024;i++)
					return_TLP.data[i] = data_[i];
			}
			else
			{
				return_TLP.data = new unsigned[length_];
				for (int i=0;i<length_;i++)
					return_TLP.data[i] = data_[i];
			}
		}
		else
		{
			return_TLP.data = NULL;
		}

		return_TLP.TLP_digest = TLP_digest_;
		if (TLP_digest_)
			return_TLP.ECRC = ECRC_;

		return return_TLP;
	}

	PCIe_TLP
	construct_TLP_Request_IO(bool with_data_,
							 PCIe_TLP_type_t type_,
							 bool TLP_digest_,
							 bool error_poisoned_,
							 byte bus_number_,
							 byte device_number_,
							 byte function_number_,
							 byte tag_,
							 byte BE_,
							 unsigned address_,
							 unsigned* data_,
							 unsigned ECRC_)
	{
		PCIe_TLP return_TLP;

		return_TLP.with_data = with_data_;
		return_TLP.DW4_header = false;

		return_TLP.header = new byte[PCIe_DOUBLE_WORD*3];

		return_TLP.header[0] = 0x0;
		if (with_data_)
			return_TLP.header[0] = return_TLP.header[0] | 0x40;
		return_TLP.header[0] = return_TLP.header[0] | (byte)(0x1F & type_);
		return_TLP.header[1] = 0x0;
		return_TLP.header[2] = 0x0;
		if (TLP_digest_)
			return_TLP.header[2] = return_TLP.header[2] | 0x80;
		if (error_poisoned_)
			return_TLP.header[2] = return_TLP.header[2] | 0x40;
		return_TLP.header[3] = 0x1;
		return_TLP.header[4] = bus_number_;
		return_TLP.header[5] = (device_number_<<3) | (0x7 & function_number_);
		return_TLP.header[6] = tag_;
		return_TLP.header[7] = 0xF & BE_;
		return_TLP.header[8] = (byte)(address_>>24);
		return_TLP.header[9] = (byte)(address_>>16);
		return_TLP.header[10] = (byte)(address_>>8);
		return_TLP.header[11] = (byte)(address_);

		return_TLP.length = 1;
		if (with_data_)
		{
			return_TLP.data = new unsigned[1];
			return_TLP.data[0] = data_[0];
		}
		else
		{
			return_TLP.data = NULL;
		}

		return_TLP.TLP_digest = TLP_digest_;
		if (TLP_digest_)
			return_TLP.ECRC = ECRC_;

		return return_TLP;
	}

	PCIe_TLP
	construct_TLP_Request_Configuration(bool with_data_,
										PCIe_TLP_type_t type_,
										bool TLP_digest_,
										bool error_poisoned_,
										byte bus_number_,
										byte device_number_,
										byte function_number_,
										byte tag_,
										byte BE_,
										byte target_bus_number_,
										byte target_device_number_,
										byte target_function_number_,
										byte target_extended_register_number_,
										byte target_register_number_,
										unsigned* data_,
										unsigned ECRC_)
	{
		PCIe_TLP return_TLP;

		return_TLP.with_data = with_data_;
		return_TLP.DW4_header = false;

		return_TLP.header = new byte[PCIe_DOUBLE_WORD*3];

		return_TLP.header[0] = 0x0;
		if (with_data_)
			return_TLP.header[0] = return_TLP.header[0] | 0x40;
		return_TLP.header[0] = return_TLP.header[0] | (byte)(0x1F & type_);
		return_TLP.header[1] = 0x0;
		return_TLP.header[2] = 0x0;
		if (TLP_digest_)
			return_TLP.header[2] = return_TLP.header[2] | 0x80;
		if (error_poisoned_)
			return_TLP.header[2] = return_TLP.header[2] | 0x40;
		return_TLP.header[3] = 0x1;
		return_TLP.header[4] = bus_number_;
		return_TLP.header[5] = (device_number_<<3) | (0x7 & function_number_);
		return_TLP.header[6] = tag_;
		return_TLP.header[7] = 0xF & BE_;
		return_TLP.header[8] = target_bus_number_;
		return_TLP.header[9] = (target_device_number_<<3) | (0x7 & target_function_number_);
		return_TLP.header[10] = 0xF & target_extended_register_number_;
		return_TLP.header[11] = target_register_number_<<2;

		return_TLP.length = 1;
		if (with_data_)
		{
			return_TLP.data = new unsigned[1];
			return_TLP.data[0] = data_[0];
		}
		else
		{
			return_TLP.data = NULL;
		}

		return_TLP.TLP_digest = TLP_digest_;
		if (TLP_digest_)
			return_TLP.ECRC = ECRC_;

		return return_TLP;
	}

	PCIe_TLP
	construct_TLP_Request_Message(bool with_data_,
								  PCIe_TLP_type_t type_,
								  byte traffic_class_,
								  bool TLP_digest_,
								  bool error_poisoned_,
								  unsigned short length_,
								  byte bus_number_,
								  byte device_number_,
								  byte function_number_,
								  byte tag_,
								  byte message_code_,
								  unsigned first_message_line_,
								  unsigned second_message_line_,
								  unsigned* data_,
								  unsigned ECRC_)
	{
		PCIe_TLP return_TLP;

		return_TLP.with_data = with_data_;
		return_TLP.DW4_header = true;

		return_TLP.header = new byte[PCIe_DOUBLE_WORD*4];

		return_TLP.header[0] = 0x0;
		if (with_data_)
			return_TLP.header[0] = return_TLP.header[0] | 0x40;
		return_TLP.header[0] = return_TLP.header[0] | 0x20;
		return_TLP.header[0] = return_TLP.header[0] | (byte)(0x1F & type_);
		return_TLP.header[1] = (byte)(0x70 & (traffic_class_<<5));
		return_TLP.header[2] = 0x0;
		if (TLP_digest_)
			return_TLP.header[2] = return_TLP.header[2] | 0x80;
		if (error_poisoned_)
			return_TLP.header[2] = return_TLP.header[2] | 0x40;
		return_TLP.header[2] = return_TLP.header[2] | (byte)(length_>>8);
		return_TLP.header[3] = (byte)(length_);
		return_TLP.header[4] = bus_number_;
		return_TLP.header[5] = (device_number_<<3) | (0x7 & function_number_);
		return_TLP.header[6] = tag_;
		return_TLP.header[7] = message_code_;
		return_TLP.header[8] = (byte)(first_message_line_>>24);
		return_TLP.header[9] = (byte)(first_message_line_>>16);
		return_TLP.header[10] = (byte)(first_message_line_>>8);
		return_TLP.header[11] = (byte)(first_message_line_);
		return_TLP.header[12] = (byte)(second_message_line_>>24);
		return_TLP.header[13] = (byte)(second_message_line_>>16);
		return_TLP.header[14] = (byte)(second_message_line_>>8);
		return_TLP.header[15] = (byte)(second_message_line_);
	
		return_TLP.length = length_;
		if (with_data_)
		{
			if (length_ == 0)
			{
				return_TLP.data = new unsigned[1024];
				for (int i=0;i<1024;i++)
					return_TLP.data[i] = data_[i];
			}
			else
			{
				return_TLP.data = new unsigned[length_];
				for (int i=0;i<length_;i++)
					return_TLP.data[i] = data_[i];
			}
		}
		else
		{
			return_TLP.data = NULL;
		}

		return_TLP.TLP_digest = TLP_digest_;
		if (TLP_digest_)
			return_TLP.ECRC = ECRC_;

		return return_TLP;
	}

	PCIe_TLP
	construct_TLP_Completion(bool with_data_,
							 PCIe_TLP_type_t type_,
							 byte traffic_class_,
							 bool TLP_digest_,
							 bool error_poisoned_,
							 bool relexed_ordering_,
							 bool no_snoop_,
							 unsigned short length_,
							 byte bus_number_,
							 byte device_number_,
							 byte function_number_,
							 byte completion_status_,
							 bool byte_count_modified_,
							 unsigned short byte_count_,
							 byte requester_bus_number_,
							 byte requester_device_number_,
							 byte requester_function_number_,
							 byte requester_tag_,
							 byte lower_address_,
							 unsigned* data_,
							 unsigned ECRC_)

	{
		PCIe_TLP return_TLP;

		return_TLP.with_data = with_data_;
		return_TLP.DW4_header = false;

		return_TLP.header = new byte[PCIe_DOUBLE_WORD*3];

		return_TLP.header[0] = 0x0;
		if (with_data_)
			return_TLP.header[0] = return_TLP.header[0] | 0x40;
		return_TLP.header[0] = return_TLP.header[0] | (byte)(0x1F & type_);
		return_TLP.header[1] = (byte)(0x70 & (traffic_class_<<5));
		return_TLP.header[2] = 0x0;
		if (TLP_digest_)
			return_TLP.header[2] = return_TLP.header[2] | 0x80;
		if (error_poisoned_)
			return_TLP.header[2] = return_TLP.header[2] | 0x40;
		if (relexed_ordering_)
			return_TLP.header[2] = return_TLP.header[2] | 0x20;
		if (no_snoop_)
			return_TLP.header[2] = return_TLP.header[2] | 0x10;
		return_TLP.header[2] = return_TLP.header[2] | (byte)(length_>>8);
		return_TLP.header[3] = (byte)(length_);
		return_TLP.header[4] = bus_number_;
		return_TLP.header[5] = (device_number_<<3) | (0x7 & function_number_);
		return_TLP.header[6] = completion_status_<<5;
		if (byte_count_modified_)
			return_TLP.header[6] = 	return_TLP.header[6] | 0x10;
		return_TLP.header[6] = return_TLP.header[6] | (byte)(byte_count_>>8);
		return_TLP.header[7] = (byte)(byte_count_);
		return_TLP.header[8] = requester_bus_number_;
		return_TLP.header[9] = (requester_device_number_<<3) | (0x7 & requester_function_number_);
		return_TLP.header[10] = requester_tag_;
		return_TLP.header[11] = lower_address_;
	
		return_TLP.length = length_;
		if (with_data_)
		{
			if (length_ == 0)
			{
				return_TLP.data = new unsigned[1024];
				for (int i=0;i<1024;i++)
					return_TLP.data[i] = data_[i];
			}
			else
			{
				return_TLP.data = new unsigned[length_];
				for (int i=0;i<length_;i++)
					return_TLP.data[i] = data_[i];
			}
		}
		else
		{
			return_TLP.data = NULL;
		}

		return_TLP.TLP_digest = TLP_digest_;
		if (TLP_digest_)
			return_TLP.ECRC = ECRC_;

		return return_TLP;
	}

	PCIe_DLL_TLP
	construct_DLL_TLP(unsigned short seq_num_,
					  PCIe_TLP tlp_,
					  unsigned LCRC_)
	{
		PCIe_DLL_TLP return_DLL_TLP;

		return_DLL_TLP.sequence_number = seq_num_;
		return_DLL_TLP.TLP.with_data = tlp_.with_data;
		return_DLL_TLP.TLP.DW4_header = tlp_.DW4_header;
		int limit;
		if (return_DLL_TLP.TLP.DW4_header)
		{
			return_DLL_TLP.TLP.header = new byte[PCIe_DOUBLE_WORD*4];
			limit = 16;
		}
		else
		{
			return_DLL_TLP.TLP.header = new byte[PCIe_DOUBLE_WORD*3];
			limit = 12;
		}
		for (int i=0;i<limit;i++)
			return_DLL_TLP.TLP.header[i] = tlp_.header[i];
		return_DLL_TLP.TLP.length = tlp_.length;
		if (return_DLL_TLP.TLP.with_data)
		{
			if (return_DLL_TLP.TLP.length == 0)
			{
				return_DLL_TLP.TLP.data = new unsigned[1024];
				for (int i=0;i<1024;i++)
					return_DLL_TLP.TLP.data[i] = tlp_.data[i];
			}
			else
			{
				return_DLL_TLP.TLP.data = new unsigned[return_DLL_TLP.TLP.length];
				for (int i=0;i<return_DLL_TLP.TLP.length;i++)
					return_DLL_TLP.TLP.data[i] = tlp_.data[i];
			}
		}
		else
		{
			return_DLL_TLP.TLP.data = NULL;
		}
		return_DLL_TLP.TLP.TLP_digest = tlp_.TLP_digest;
		return_DLL_TLP.TLP.ECRC = tlp_.ECRC;
		return_DLL_TLP.LCRC = LCRC_;
		delete[] tlp_.header;
		if (return_DLL_TLP.TLP.with_data)
			delete[] tlp_.data;

		return return_DLL_TLP;
	}

	PCIe_DLLP
	construct_DLLP_ACK_NAK(PCIe_DLLP_type_t type_,
						   unsigned short sequence_number_,
						   unsigned short CRC_)
	{
		PCIe_DLLP return_DLLP;

		return_DLLP.type = type_;
		return_DLLP.data[0] = 0x0;
		return_DLLP.data[1] = (byte)(sequence_number_>>8);
		return_DLLP.data[2] = (byte)(sequence_number_);
		return_DLLP.CRC = CRC_;

		return return_DLLP;
	}

	PCIe_DLLP
	construct_DLLP_FC(PCIe_DLLP_type_t type_,
					  byte header_FC_,
					  unsigned short data_FC_,
					  unsigned short CRC_)
	{
		PCIe_DLLP return_DLLP;

		return_DLLP.type = type_;
		return_DLLP.data[0] = header_FC_>>2;
		return_DLLP.data[1] = (header_FC_<<6) | (byte)(data_FC_>>8);
		return_DLLP.data[2] = (byte)(data_FC_);
		return_DLLP.CRC = CRC_;

		return return_DLLP;
	}

	PCIe_DLLP
	construct_DLLP_PM(PCIe_DLLP_type_t type_,
					  unsigned short CRC_)
	{
		PCIe_DLLP return_DLLP;

		return_DLLP.type = type_;
		for (int i=0;i<3;i++)
			return_DLLP.data[i] = 0x0;
		return_DLLP.CRC = CRC_;

		return return_DLLP;
	}

	PCIe_DLLP
	construct_DLLP_Vendor(PCIe_DLLP_type_t type_,
						  unsigned vendor_specific_,
						  unsigned short CRC_)
	{
		PCIe_DLLP return_DLLP;

		return_DLLP.type = type_;
		return_DLLP.data[0] = (byte)(vendor_specific_>>16);
		return_DLLP.data[0] = (byte)(vendor_specific_>>8);
		return_DLLP.data[2] = (byte)(vendor_specific_);
		return_DLLP.CRC = CRC_;

		return return_DLLP;
	}
};

#endif